package com.xiaoyudeguang.interceptor;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.xiaoyudeguang.annotation.JsonField;
import com.xiaoyudeguang.model.Model;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;

/**
 * ObjectNode字段映射到实体属性
 *
 * @author 赵光
 */
@Component
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class ResultInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (Objects.isNull(result)) {
            return null;
        }
        if (result instanceof ArrayList) {
            for (Object data : ((ArrayList<?>) result)) {
                Field[] fields = data.getClass().getDeclaredFields();
                if (data instanceof Model) {
                    JsonNode jsonNode = ((Model) data).getJson();
                    if (jsonNode != null && jsonNode.size() > 0) {
                        for (Field jsonField : fields) {
                            JsonField jsonFieldAnno = jsonField.getDeclaredAnnotation(JsonField.class);
                            if (jsonFieldAnno != null && jsonFieldAnno.exist()) {
                                jsonField.setAccessible(true);
                                if (jsonNode.has(jsonField.getName())) {
                                    try {
                                        jsonField.set(data, getValue(jsonNode, jsonField));
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
                for (Field field : fields) {
                    if (field.getType() == ObjectNode.class) {
                        field.setAccessible(true);
                        JsonNode jsonNode = (ObjectNode) field.get(data);
                        if (jsonNode != null && jsonNode.size() > 0) {
                            for (Field jsonField : fields) {
                                if (field != jsonField) {
                                    JsonField jsonFieldAnno = jsonField.getDeclaredAnnotation(JsonField.class);
                                    if (jsonFieldAnno != null && jsonFieldAnno.exist()) {
                                        jsonField.setAccessible(true);
                                        if (jsonNode.has(jsonField.getName())) {
                                            try {
                                                jsonField.set(data, getValue(jsonNode, field));
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } else if (result instanceof Model) {
            Model model = (Model) result;
            Field[] fields = model.getClass().getDeclaredFields();
            JsonNode jsonNode = model.getJson();
            if (jsonNode != null && jsonNode.size() > 0) {
                for (Field jsonField : fields) {
                    if (jsonField.getDeclaredAnnotation(JsonField.class) != null) {
                        jsonField.setAccessible(true);
                        if (jsonNode.has(jsonField.getName())) {
                            try {
                                jsonField.set(result, getValue(jsonNode, jsonField));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } else if (result.getClass() == ObjectNode.class) {
            Field[] fields = result.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() == ObjectNode.class) {
                    field.setAccessible(true);
                    JsonNode jsonNode = (ObjectNode) field.get(result);
                    if (jsonNode != null && jsonNode.size() > 0) {
                        for (Field jsonField : fields) {
                            if (field != jsonField) {
                                JsonField jsonFieldAnno = jsonField.getDeclaredAnnotation(JsonField.class);
                                if (jsonFieldAnno != null && jsonFieldAnno.exist()) {
                                    jsonField.setAccessible(true);
                                    if (jsonNode.has(jsonField.getName())) {
                                        try {
                                            jsonField.set(result, getValue(jsonNode, field));
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    public static Object getValue(JsonNode jsonNode, Field field) {
        if (Boolean.class == field.getType()) {
            return jsonNode.path(field.getName()).asBoolean();
        } else if (Long.class == field.getType()) {
            return jsonNode.path(field.getName()).asLong();
        } else if (Integer.class == field.getType()) {
            return jsonNode.path(field.getName()).asInt();
        } else if (Double.class == field.getType()) {
            return jsonNode.path(field.getName()).asDouble();
        } else if (LocalDateTime.class == field.getType()) {
            return LocalDateTime.ofEpochSecond(jsonNode.path(field.getName()).asLong() / 1000, 0, ZoneOffset.ofHours(8));
        } else if (LocalDate.class == field.getType()) {
            return LocalDate.ofEpochDay(jsonNode.path(field.getName()).asLong() / 1000);
        } else if (ArrayNode.class == field.getType()) {
            return jsonNode.withArray(field.getName());
        } else if (JsonNode.class == field.getType()) {
            return jsonNode.path(field.getName());
        }
        return jsonNode.path(field.getName()).asText();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }


    @Override
    public void setProperties(Properties properties) {


    }
}